#include <cstring>
#include "MyString.h"
#include <iostream>

MyString::MyString()// 默认构造
{
    // 初始化
    std::cout << "无参构造" << std::endl;
    this->size = 0;
    this->capacity = 15;
    this->s = new char[this->capacity];// 动态分配内存

}

MyString::MyString(const char* str)// 带参构造
{
    std::cout << "带参 构造" << std::endl;
    this->size = strlen(str);
    this->capacity = this->size + 1;// \0 占一个位置
    this->s = new char[this->capacity];// 动态分配内存
    strcpy(this->s, str);// 字符串拷贝
}

MyString::~MyString()// 析构
{
    delete[] this->s;// 释放内存
}

MyString::MyString(const MyString& str)// 拷贝构造
{
    std::cout << "拷贝构造" << std::endl;
    this->size = str.size;
    this->capacity = str.capacity;
    this->s = new char[this->capacity];// 动态分配内存
    strcpy(this->s, str.s);// 字符串拷贝

}

// 返回的是引用: 为了可以连续赋值 以及 防止拷贝
MyString& MyString::operator=(const MyString& str)// 赋值运算符重载
{
    std::cout << "赋值运算符重载" << std::endl;
    if (this == &str)// 防止自赋值
    {
        return *this;
    }
    delete[] this->s;// 先把原有对象释放掉
    this->size = str.size;
    this->capacity = str.capacity;
    this->s = new char[this->capacity];// 动态分配内存
    strcpy(this->s, str.s);// 字符串拷贝
    return *this;// 返回的是左值
}

MyString& MyString::operator+=(const MyString& str)// 重载+=运算符
{
    // 此时的大小 应该 先被更新
    // 然后 才能进行拼接
    this->size += str.size;
    // 空间不够
    // 1. 扩容
    if (this->size >= this->capacity)
    {
        this->capacity = this->size + 1;// 扩容
        char* newP = new char[this->capacity];// 动态分配内存
        strcpy(newP, this->s);// 复制
        delete[] this->s;// 释放原有内存
        this->s = newP;// 更新指针
    }

    // 2. 拼接
    strcat(this->s, str.s);// 字符串拼接
    return *this;
}

//重载的operator+=操作符，用于将一个字符c添加到MyString对象的末尾
void MyString::operator+=(const char& c)
{
    if(this->size >= this->capacity)
    {
        this->capacity = this->size + 1;// 扩容
        char* newP = new char[this->capacity];// 新空间
        strcpy(newP, this->s);// 复制
        delete[] this->s;// 释放原有内存
        this->s = newP;// 更新指针
    }
    this->s[this->size++] = c;// 末尾添加字符
    this->s[this->size] = '\0';// 添加字符串结束符
}

// 重载 + 运算符以用于 字符串类时，通常返回一个新的字符串对象 而不是引用
// 因为返回引用可能会导致许多问题，特别是 涉及到 局部变量的 生命周期和内存管理
/*
    局部变量的生命周期：

        在操作中创建的对象是局部变量，如果返回它的引用，那么当函数结束时，
        该局部变量会被销毁，导致引用悬空（悬垂引用）

    防止内存管理问题：

        返回引用可能导致难以管理内存，特别是在涉及动态分配内存的情况下。
        返回新对象可以更好地利用C++的RAII（Resource Acquisition Is Initialization）惯用法，
        通过构造和析构函数来管理资源。
*/

MyString MyString::operator+(const MyString& str)// 重载+运算符
{
    MyString res(*this);// 拷贝构造
    res += str;// 重载+=运算符
    return res;// 返回新对象
}

// 重载 -= 运算符
// 字符串删除的逻辑
MyString& MyString::operator-=(const MyString& str)// 重载 -= 运算符
{
    char *dest = strstr(this->s, str.s);// 找到一个匹配的位置
    while(dest != nullptr)
    {
        char *back = dest + str.size;// 设置一个指针 back，指向匹配字符串 str.s 之后的位置
                                    // 将 dest 指针向后移动 str.size 个字符
                                    // 这样，back 指针就指向了匹配字符串之后的第一个字符
        while(*back != '\0')// 移动 back 指针，直到遇到字符串结束符
        {
            *dest = *back;// 将 back 指针指向的字符赋值给 dest 指针指向的字符
            dest++;// 移动 dest 指针
            back++;// 移动 back 指针
        }
        *dest = '\0';// 将 dest 指针指向的字符设置为字符串结束符
        this->size -= str.size;// 更新字符串的大小
        dest = strstr(dest, str.s);// 继续查找下一个匹配的位置
    }
    return *this;
}

MyString MyString::operator-(const MyString& str)// 重载 - 运算符
{
    MyString result(*this);// 拷贝构造 构造一个对象放在result里面
    result -= str;// 重载 -= 运算符
    return result;
}

// 重载 [] 运算符: 返回值是引用 可以修改
char& MyString::operator[](int index)// 重载 [] 运算符
{
    return this->s[index];
}

bool MyString::operator==(const MyString& str)// 重载 == 运算符
{
    return strcmp(this->s, str.s) == 0;
}

bool MyString::operator!=(const MyString& str)// 重载 != 运算符
{
    return strcmp(this->s, str.s) != 0;
}

bool MyString::operator>(const MyString& str)// 重载 > 运算符
{
    return strcmp(this->s, str.s) > 0;
}

bool MyString::operator<(const MyString& str)// 重载 < 运算符
{
    return strcmp(this->s, str.s) < 0;
}

bool MyString::operator>=(const MyString& str)// 重载 >= 运算符
{
    return strcmp(this->s, str.s) >= 0;
}

bool MyString::operator<=(const MyString& str)// 重载 <= 运算符
{
    return strcmp(this->s, str.s) <= 0;
}

bool MyString::operator&&(const MyString& str)// 重载 && 运算符
{
    return this->size != 0 && str.size != 0;
}

bool MyString::operator||(const MyString& str)// 重载 || 运算符
{
    return this->size != 0 || str.size != 0;
}


// 全局友元 重载 <<   >>
std::ostream& operator<<(std::ostream& os, const MyString& str)// 输出运算符重载
{
    os << str.s;// 输出字符串
    return os;// 返回引用 是为了可以连续输出
}

// 如果加了const 就改变不了了
std::istream& operator>>(std::istream& is, MyString& str)// 输入运算符重载
{
    // 有一个更好的通用写法
    // 虽然不知道输入的字符有多大
    // 但是肯定以\n 结束
    char c = 0;
    while((c = getchar()) != '\n')
    {
        // 一个个塞，肯能会越界，所以写一个扩容函数
        str += c;
    }
    return is;
}   